package top.codetheory.thread._005_交替输出;

import java.util.Optional;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: CodeTheory
 * @since: 2024/5/7 上午9:40
 **/
@Slf4j
public class _001_wait_notify版 {

    public static void main(String[] args) {
//        test1();
//        test2();
        test3();
    }

    /**
     * <img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1715047583955-2024-5-710:06:24.png"  />
     * <img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1715046946959-2024-5-709:55:46.png"  />
     */
    public static void test1() {
        SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
        new Thread(() -> {
            syncWaitNotify.print("a", 1, 2);
        }, "t1").start();
        new Thread(() -> {
            syncWaitNotify.print("b", 2, 3);
        }, "t2").start();
        new Thread(() -> {
            syncWaitNotify.print("c", 3, 1);
        }, "t3").start();
    }

    /**
     * <img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1715053708311-2024-5-711:48:28.png"  />
     */
    public static void test2() {
        AwaitSignal awaitSignal = new AwaitSignal(5);
        // 三个休息室
        Condition t1 = awaitSignal.newCondition();
        Condition t2 = awaitSignal.newCondition();
        Condition t3 = awaitSignal.newCondition();
        new Thread(() -> {
            awaitSignal.print("a", t1, t2);
        }, "t1").start();
        new Thread(() -> {
            awaitSignal.print("b", t2, t3);
        }, "t2").start();
        new Thread(() -> {
            awaitSignal.print("c", t3, t1);
        }, "t3").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        awaitSignal.lock();
        t1.signal();
        awaitSignal.unlock();

    }

    static Thread t1, t2, t3;

    /**
     * <p> todo 解释为什么静态变量可以, 局部变量不行
     * <p><img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1715065877619-2024-5-715:11:17.png"  />
     */
    public static void test3() {
        ParkUnPark parkUnPark = new ParkUnPark(5);

        t1 = new Thread(() -> {
            parkUnPark.print("a", t2);
        }, "t1");
        t2 = new Thread(() -> {
            parkUnPark.print("b", t3);
        }, "t2");
        t3 = new Thread(() -> {
            parkUnPark.print("c", t1);
        }, "t3");

        t1.start();
        t2.start();
        t3.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        LockSupport.unpark(t1);
    }

}

@Slf4j
@AllArgsConstructor
class SyncWaitNotify {
    private int flag;
    private int loopNumber;

    public void print(String info, int currentFlag, int nextFlag) {
        synchronized (this) {
            for (int i = 0; i < loopNumber; i++) {
                while (flag != currentFlag) {  // 防止虚假唤醒, 只有 flag 为 currentFlag的才输出, 否则循环等待
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                log.info(info);
                flag = nextFlag;
                this.notifyAll();
            }
        }
    }
}

@Slf4j
@AllArgsConstructor
class AwaitSignal extends ReentrantLock {
    private int loopNumber;

    public void print(String info, Condition curr, Condition next) {
        for (int i = 0; i < loopNumber; i++) {
            lock();
            try {
                curr.await();
                log.info(info);
                next.signal();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                unlock();
            }
        }
    }
}

@Slf4j
@AllArgsConstructor
class ParkUnPark {
    private int loopNumber;

    public void print(String info, Thread next) {
        log.debug("在print内{}的地址:{}", Optional.of(info).map(i -> {
            switch (i) {
                case "a":
                    return "t2";
                case "b":
                    return "t3";
                case "c":
                    return "t1";
            }
            return null;
        }).get(), System.identityHashCode(next));
        for (int i = 0; i < loopNumber; i++) {
            LockSupport.park();
            log.info(info);
            LockSupport.unpark(next);
        }
    }
}